/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.bff.designtime.generator.defaultvalue.converter.method;

import com.fasterxml.jackson.core.JsonParser;
import com.inspur.edp.bff.designtime.generator.VmGeneratorContext;
import com.inspur.edp.bff.designtime.generator.common.VMChangeJsonConvertorGenUtils;
import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.cef.api.message.CefException;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.generator.overall.GeneratorUtil;
import com.inspur.edp.cef.spi.jsonser.base.StringUtils;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import lombok.var;
import org.eclipse.jdt.core.dom.*;
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;

public class GetDefaultValueGenerator extends ClassMethodGenerator {

    private static final String param_PropertyName = "propertyName";
    private static final String param_PropertyValue = "propertyValue";

    private VmGeneratorContext vmContext;

    public GetDefaultValueGenerator(VmGeneratorContext vmContext) {
        this.vmContext = vmContext;
    }

    private void buildSwitchLables(
            AST ast, SwitchStatement switchStatement, String propertyName) {
        SwitchCase propNameCase = ast.newSwitchCase();
        StringLiteral propNameLiteral = ast.newStringLiteral();
        propNameLiteral.setLiteralValue(propertyName);
        propNameCase.setExpression(propNameLiteral);
        switchStatement.statements().add(propNameCase);
        String camelPropName = StringUtils.toCamelCase(propertyName);
        if (camelPropName != propertyName) {
            SwitchCase camelPropNameCase = ast.newSwitchCase();
            StringLiteral camelPropNameLiteral = ast.newStringLiteral();
            camelPropNameLiteral.setLiteralValue(camelPropName);
            camelPropNameCase.setExpression(camelPropNameLiteral);
            switchStatement.statements().add(camelPropNameCase);
        }
    }

    //region MethodBody
    @Override
    protected Block buildMethodBody() {
        SwitchStatement result = getSwitchBlock();
        addCaseBlocks(result);
        getDefaultStatement(result);
        Block block = ast.newBlock();
        block.statements().add(result);
        return block;
    }

    private SwitchStatement getSwitchBlock() {
        SwitchStatement switchStatement = ast.newSwitchStatement();
        switchStatement.setExpression(ast.newSimpleName(param_PropertyName));
        return switchStatement;
    }

    private void addCaseBlocks(SwitchStatement result) {
        for (IGspCommonField item : vmContext.getVm().getMainObject().getContainElements()) {
            addCaseBlock((IGspCommonElement) item, result);
        }
    }

    private void addCaseBlock(IGspCommonElement vmElement, SwitchStatement result) {
        if (vmElement.getIsUdt() || vmElement.getObjectType() == GspElementObjectType.DynamicProp) {
            //TODO 暂不支持udt默认值
            return;
        }
        buildSwitchLables(ast, result, vmElement.getLabelID());
        ReturnStatement returnStatement = ast.newReturnStatement();
        var caseStatement = getElementCase(vmElement);
        if (caseStatement == null)
            return;
        returnStatement.setExpression(caseStatement);
        result.statements().add(returnStatement);
    }

  private Expression getElementCase(IGspCommonElement vmElement) {
    Expression invocationExpress = null;
    switch (vmElement.getObjectType()) {
      case None:
        invocationExpress = readBasicProperty(vmElement);
        break;
      case Association:
        invocationExpress = readAssProperty(vmElement);
        break;
      case Enum:
        invocationExpress = readEnumProperty(vmElement);
        break;
      default:
        throw new RuntimeException();
    }
    return invocationExpress;
  }

    //region readProperty
    private MethodInvocation readBasicProperty(IGspCommonElement element) {
        switch (element.getMDataType()) {
            case String:
            case Text:
                return readStringProperty(element);
            case Integer:
                return readIntProperty(element);
            case Decimal:
                return readDecimalProperty(element);
            case Date:
            case DateTime:
                return readDateProperty(element);
            case Boolean:
                return readBoolProperty(element);
            case Binary:
                return readBinaryProperty(element);
            default:
                throw new RuntimeException(
                        "错误的类型" + element.getMDataType().toString());

        }
    }

    private MethodInvocation readProperty(IGspCommonElement element, String methodName) {
        MethodInvocation methodInvocation = ast.newMethodInvocation();
        methodInvocation.setName(ast.newSimpleName(methodName));
        var castExpression = ast.newCastExpression();
        castExpression.setExpression(ast.newSimpleName(param_PropertyValue));
        castExpression.setType(new TypeInfo(JsonParser.class).getType(ast, vmContext.getEntityContext()));
        methodInvocation.arguments().add(castExpression);
        return methodInvocation;
    }

    private MethodInvocation readStringProperty(IGspCommonElement element) {
        return readProperty(element, VMChangeJsonConvertorGenUtils.mName_ReadString);
    }

    private MethodInvocation readIntProperty(IGspCommonElement element) {
        return readProperty(element, VMChangeJsonConvertorGenUtils.mName_ReadInt);
    }

    private MethodInvocation readDecimalProperty(IGspCommonElement element) {
        return readProperty(element, VMChangeJsonConvertorGenUtils.mName_ReadDecimal);
    }

    private MethodInvocation readDateProperty(IGspCommonElement element) {
        return readProperty(element, VMChangeJsonConvertorGenUtils.mName_ReadDateTime);
    }

    private MethodInvocation readBoolProperty(IGspCommonElement element) {
        return readProperty(element, VMChangeJsonConvertorGenUtils.mName_ReadBool);
    }

    private MethodInvocation readBinaryProperty(IGspCommonElement element) {
        return readProperty(element, VMChangeJsonConvertorGenUtils.mName_ReadBytes);
    }

  private MethodInvocation readAssProperty(IGspCommonElement element) {
    MethodInvocation invocation = ast.newMethodInvocation();
    invocation
        .setName(ast.newSimpleName(VMChangeJsonConvertorGenUtils.mName_ReadAssociation));
    var castExpression = ast.newCastExpression();
    castExpression.setExpression(ast.newSimpleName(param_PropertyValue));
    castExpression.setType(new TypeInfo(JsonParser.class).getType(ast, vmContext.getEntityContext()));
    invocation.arguments().add(castExpression);

    TypeLiteral type = ast.newTypeLiteral();
    type.setType(ast.newSimpleType(ast.newSimpleName(element.getAssociationTypeName())));
    invocation.arguments().add(type);
    return invocation;
  }

    private MethodInvocation readEnumProperty(IGspCommonElement element) {
        MethodInvocation invocation = ast.newMethodInvocation();
        invocation.setName(ast.newSimpleName(VMChangeJsonConvertorGenUtils.mName_ReadEnum));

        var castExpression = ast.newCastExpression();
        castExpression.setExpression(ast.newSimpleName(param_PropertyValue));
        castExpression.setType(new TypeInfo(JsonParser.class).getType(ast, vmContext.getEntityContext()));
        invocation.arguments().add(castExpression);
        TypeLiteral type = ast.newTypeLiteral();
        type.setType(ast.newSimpleType(ast.newSimpleName(element.getEnumTypeName())));
        invocation.arguments().add(type);
        return invocation;
    }

    //endregion
    private void getDefaultStatement(SwitchStatement result) {
        var caseExpression = ast.newSwitchCase();
        caseExpression.setExpression(null);
        result.statements().add(caseExpression);
        addImport(CefException.class.getTypeName());
        ThrowStatement throwStatement = GeneratorUtil.getThrowStatement(ast,null);
//        ClassInstanceCreation creation = ast.newClassInstanceCreation();
//        creation.setType(new TypeInfo(RuntimeException.class).getType(ast, vmContext.getEntityContext()));
//        ThrowStatement throwStatement = ast.newThrowStatement();
//        throwStatement.setExpression(creation);
        result.statements().add(throwStatement);
    }

    //endregion
    @Override
    protected boolean getIsOverride() {
        return true;
    }

    @Override
    protected java.util.ArrayList<ModifierKeyword> getAccessModifiers() {
        return new java.util.ArrayList<ModifierKeyword>(java.util.Arrays.asList(
                new ModifierKeyword[]{ModifierKeyword.PROTECTED_KEYWORD}));
    }

    @Override
    protected String getMethodName() {
        return "getDefaultValue";
    }

    @Override
    protected java.util.ArrayList<ParameterInfo> getParameterCollection() {
        ParameterInfo namePar = new ParameterInfo();
        namePar.setParamName(param_PropertyName);
        namePar.setParamType(new TypeInfo(String.class));
        ParameterInfo valuePar = new ParameterInfo();
        valuePar.setParamName(param_PropertyValue);
        valuePar.setParamType(new TypeInfo(Object.class));
        return new java.util.ArrayList<ParameterInfo>(java.util.Arrays.asList(
                new ParameterInfo[]{namePar, valuePar}));
    }

    @Override
    protected TypeInfo getReturnType() {
        return new TypeInfo(Object.class);
    }
}
